{\rtf0\ansi{\fonttbl\f0\fmodern Courier;\f1\ftech Symbol;\f2\fmodern Ohlfs;}
\margl40
\margr40
{\colortbl;\red0\green0\blue0;}
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i0\ulnone\fs24\fc0\cf0 //ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//
\i 	
\b FILENAME
\b0 :	
\b\i0 Navigator.m\

\b0 //
\i 	
\b SUMMARY
\b0 :	
\b\i0 Implementation of the Navigator, which manages & locates eTDocs
\b0 \
//	
\b\i SUPERCLASS
\b0 :
\i0 	
\b Object:Navigator
\b0 \
//	
\b\i PROTOCOLS
\b0 :
\i0 	
\b <DocNotification>
\b0 \
//	
\b\i INTERFACE
\b0 :
\i0 	
\b Navigator.nib
\b0 \
//	
\b\i AUTHOR
\b0 :		
\b\i0 Rohit Khare
\b0 \
//	
\b\i COPYRIGHT
\b0 :	
\f1\i0 с
\f0\b 1993,94 California Institure of Technology, eText Project\

\b0 //ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b\i Implementation Comments
\b0\i0 \
//		The viewPopup methods are unfinished.\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b\i HISTORY
\b0\i0 \
//	11/20/94:	
\b Split into 3 panels.
\b0 \
//	10/31/94:	
\b Added the ability to inspect docInfos directly from the Nav.
\b0 \
//	09/25/94:	
\b Revamped for eText5
\b0 \
//	08/21/93:	
\b Created. Gutless object
\b0 \
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Imported Interfaces
\b0 \
//\
	#import "
\b Navigator.h
\b0 "\
	#import "
\b Document
\b0 .subproj/
\b eTDocInfoBrowserCell.h
\b0 "\
	#import "
\b Kludges
\b0 .subproj/
\b regexpr.h
\b0 "\
\

\i @implementation Navigator\

\i0 //ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Class Management
\b0 \
//\
- 
\b init
\b0  \{\
	NXRect r;\
	\
	[NXApp 
\b loadNibSection
\b0 :"
\b Navigator.nib
\b0 " owner:self withNames:NO];\
\
	[navPanel 
\b setExcludedFromWindowsMenu
\b0 :YES];\
	[navPanel setFrameAutosaveName:"etNavigatorPanel"];\
	[navPanel 
\b setFrameUsingName
\b0 :"etNavigatorPanel"];\

\i 	
\i0 //
\i if ([userModel boolQuery:"NavigatorFloat"])\
	
\i0 //
\i 	[navPanel setFloatingPanel:YES];\

\i0 	[
\b navBrowser
\b0  
\b setDoubleAction
\b0 :[navBrowser 
\b action
\b0 ]];\
	[navBrowser 
\b setAction
\b0 :@selector(
\b inspect:
\b0 )];\
	// 
\i This stupidity is documented in the NXBrowser Docs
\i0 \
	[navBrowser 
\b setMinColumnWidth
\b0 :1];\
	[navBrowser 
\b getFrame
\b0 :&r];\
	[navBrowser 
\b setMaxVisibleColumns
\b0 :floor(
\b NX_WIDTH
\b0 (&r)/
\b 140
\b0 )];\
	[navBrowser 
\b setMinColumnWidth
\b0 :
\b 140
\b0 ];\

\i 	[navBrowser setMaxVisibleColumns:10];\

\i0 	[navBrowser getTitleFromPreviousColumn:YES];\
	[
\b navBrowser setPathSeparator
\b0 :
\fc1\cf1 '
\b `
\b0 '
\fc0\cf0 ];\
	[
\b navBrowser setCellClass
\b0 :
\fc1\cf1 [
\b eTDocInfoBrowserCell
\b0  class]
\fc0\cf0 ];\
	[
\b navWell initLinkWell
\b0 ];\
\
	[btPanel 
\b setExcludedFromWindowsMenu
\b0 :YES];\
	[btPanel setFrameAutosaveName:"etHistoryPanel"];\
	[btPanel 
\b setFrameUsingName
\b0 :"etHistoryPanel"];\
	[
\b btBrowser
\b0  
\b setDoubleAction
\b0 :[btBrowser 
\b action
\b0 ]];\
	[btBrowser setAction:NULL];\
	[
\b btBrowser setCellClass
\b0 :
\fc1\cf1 [
\b eTDocInfoBrowserCell
\b0  class]
\fc0\cf0 ];\
	
\b btList
\b0  =[[List alloc] 
\b init
\b0 ];\
	
\b btDirty
\b0  = 
\b navDirty
\b0  = 
\b YES
\b0 ;\
	
\b shouldUseQP 
\b0 = 
\b NO
\b0 ;\
\
	[
\b navBrowser
\b0  
\b loadColumnZero
\b0 ];\
	return self;\
\}\
\
- 
\b free
\b0  \{\
	navPanel = [
\b navPanel
\b0  free];\
	btList = [
\b btList
\b0  free];\
	return self = [
\b super
\b0  free];\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b User Interface
\b0 \
//\
\
- 
\b showNavigator
\b0  \{\
	[
\b navPanel makeKeyAndOrderFront
\b0 :self];\
	[navPanel makeFirstResponder:navBrowser];\
	return self;\
\}\
\
- 
\b showHistory
\b0  \{\
	[
\b btPanel makeKeyAndOrderFront
\b0 :self];\
	[btPanel makeFirstResponder:btBrowser];\
	return self;\
\}\
\
- 
\b backtrack
\b0 :sender \{\
	// 
\i open the backtracked file -- make sure it eventually pops up in front,
\i0 \
	//
\i  even if the doc is already open. Q: will BTlist "bubble" this entry up?
\i0 \
	[
\b self
\b0  
\b navigate
\b0 :sender];\
	return self;\
\}\
 \
- 
\b navigate
\b0 :sender \{\
	long dID;\
	\
	// 
\i open the navigated file -- make sure it eventually pops up in front,\

\i0 	// 
\i even if the doc is already open.
\i0 \
	if (dID = [[
\b sender
\b0  
\b selectedCell
\b0 ] 
\b docID
\b0 ])\
		[
\b etApp openID
\b0 :dID];\
	return self;\
\}\
\
- 
\b inspect
\b0 :sender \{\
	long dID;\
	\
	if (dID = [[
\b sender
\b0  
\b selectedCell
\b0 ] 
\b docID
\b0 ]) \{\
		etfLink el;\
		id theDI = [
\b eTDocInfo
\b0  findDocInfo:
\b dID
\b0 ];\
		\
		[
\b inspector
\b0  
\b inspect
\b0 :theDI];\
		el.docID = [theDI docIDStr];\
		el.docTitle = [theDI docTitle];\
		el.anchorID = NXUniqueString("0");\
		el.anchorTitle = [theDI docTitle];\
		[
\b navWell
\b0  
\b setLink
\b0 :&el];\
	\} else \{\
		[
\b navWell
\b0  
\b setLink
\b0 :NULL];\
	\}\
	return self;\
\}\
\
- 
\b runQuery
\b0 :sender \{\
	
\b shouldUseQP
\b0  = 
\b YES
\b0 ;\
	[
\b navBrowser
\b0  
\b loadColumnZero
\b0 ];\
	return self;\
\}\
\
- 
\b reset
\b0 :sender \{\
	
\b shouldUseQP
\b0  = 
\b NO
\b0 ;\
	
\b navDirty
\b0  = 
\b YES
\b0 ;\
	return self;\
\}\
- 
\b touch
\b0 \{\
	
\b btDirty
\b0  = 
\b navDirty
\b0  = 
\b YES
\b0 ;\
	return self;\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Doc Notification
\b0 \
//\
- 
\b docWillOpen
\b0 :etDoc \{\
	id theDocInfo = [etDoc docInfo];\
	if ([
\b btList
\b0  
\b indexOf
\b0 :[
\b theDocInfo docID
\b0 ]] == 
\b NX_NOT_IN_LIST
\b0 ) \{\
		
\b navDirty
\b0  = YES;\
		
\b btDirty
\b0  = YES;\
	\} else \{\
		[btList 
\b removeObject
\b0 :[theDocInfo
\b  docID
\b0 ]];\
	\}\
	[btList 
\b insertObject
\b0 :[theDocInfo 
\b docID
\b0 ] at:
\b 0
\b0 ];\
	
\b btDirty
\b0  = YES;\
	return self;\
\}\
\
- 
\b docDidWrite
\b0 :etDoc\{\
	btDirty = 
\b YES
\b0 ;\
	navDirty = 
\b YES
\b0 ;\
	return self;\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Filesystem Querying
\b0 \
//\
- 
\b find
\b0 :(const char *) 
\b regex
\b0  
\b inLibrary
\b0 :(const char *) 
\b lib\
  target
\b0 :(id) target 
\b action
\b0 :(SEL) action \{\
	
\b char
\b0  
\b temp
\b0 [MAXPATHLEN];\
\
	sprintf(temp, "
\b %s
\b0 ", [[NXBundle 
\b mainBundle
\b0 ] 
\b directory
\b0 ]);\
	[self find:regex inDir:temp target:target action:action];\
	sprintf(temp, "
\b %s/Library/%s
\b0 ", 
\b NXHomeDirectory
\b0 (), 
\b lib
\b0 );\
	[self find:regex inDir:temp target:target action:action];\
	sprintf(temp, "
\b /LocalLibrary/%s
\b0 ", 
\b lib
\b0 );\
	[self find:regex inDir:temp target:target action:action];\
	sprintf(temp, "
\b /NextLibrary/%s
\b0 ", 
\b lib
\b0 );\
	[self find:regex inDir:temp target:target action:action];\
	return self;\
\}\
\
- 
\b find
\b0 :(const char *) 
\b regex
\b0  
\b inDir
\b0 :(const char *) 
\b dir
\b0  \
  
\b target
\b0 :(id) target 
\b action
\b0 :(SEL) action \{\
	
\b char
\b0 			*
\b s
\b0 , 
\b aPath
\b0 [MAXPATHLEN];\
	
\b DIR
\b0 				*
\b dirp
\b0 ;\
	struct 
\b direct
\b0 	*
\b dp
\b0 ;\
	\
	
\b dirp
\b0  = 
\b opendir
\b0 (
\b dir
\b0 );\
	if (!
\b dirp
\b0 ) return 
\b nil
\b0 ;\
	if (
\b s
\b0 =
\b re_comp
\b0 (
\b regex
\b0 ))\
		\{NXLogError("
\b Regex Compilation Error: 
\b0 %s",
\b s
\b0 ); return 
\b nil
\b0 ;\}\
	for (
\b dp
\b0  = 
\b readdir
\b0 (
\b dirp
\b0 ); 
\b dp != NULL
\b0 ; dp = readdir(dirp))\
		if (
\b re_exec
\b0 (
\b dp->d_name
\b0 )==1) \{\
			sprintf(
\b aPath
\b0 , "%s/%s", 
\b dir
\b0 , 
\b dp->d_name
\b0 );\
			[
\b target perform
\b0 :
\b action
\b0  
\b with
\b0 :(void *)NXUniqueString(
\b aPath
\b0 )];\
		\}\
	
\b closedir
\b0 (
\b dirp
\b0 );\
	return self;\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Database Management
\b0 \
//\
\
- (
\b BOOL
\b0 )
\b isParent
\b0 :aDocInfo \{\
	id			
\b theDocInfo
\b0 ;\
	HashTable	*
\b docInfoTable;
\b0 \
	long 		key;\
	NXHashState state;\
	
\b BOOL
\b0 		
\b isParent
\b0 =
\b NO
\b0 ;\
	NXAtom		theQuery;\
\
	docInfoTable = [
\b eTDocInfo docInfoTable
\b0 ];\
	state = [
\b docInfoTable
\b0  
\b initState
\b0 ];\
	
\b theQuery
\b0  = [aDocInfo 
\b docIDStr
\b0 ];\
	
\b while
\b0  (([docInfoTable 
\b nextState
\b0 :&state key:(void **) &key\
			value: (void**) &theDocInfo]) 
\b &&
\b0  (
\b !isParent
\b0 ))\
		if ([
\b theDocInfo
\b0  
\b searchFor
\b0 :NXUniqueString(
\b theQuery
\b0 ) \
						
\b in
\b0 :NXUniqueString(
\b PARENT
\b0 )])\
			isParent = YES;\
	
\b return isParent
\b0 ;\
\}\
\
- (
\b List
\b0  *)
\b btList
\b0  \{\
	static List*
\b theList
\b0 =nil;\
	int			i,j;\
	id			
\b theDocInfo
\b0 ;\
	\
	if (!theList) 
\b theList
\b0  = [[
\b List
\b0  alloc] 
\b init
\b0 ];\
	[
\b theList
\b0  
\b empty
\b0 ];\
	j = [btList count];\
	for (i=0; i<j; i++) \{\
		
\b theDocInfo
\b0  = [
\b eTDocInfo
\b0  
\b findDocInfo
\b0 :[
\b btList
\b0  objectAt:
\b i
\b0 ]];\
		if (
\b theDocInfo
\b0 )\
			[
\b theList
\b0  
\b addObject
\b0 :theDocInfo];\
		else \{\
			[
\b btList
\b0  
\b removeObjectAt
\b0 :i];\
			j--;\
			i--;\
		\}\
	\}\
	
\b return
\b0  
\b theList
\b0 ;\
\}\
\
- (
\b List
\b0  *)
\b query
\b0 :(const char *)
\b theQuery
\b0  
\b field
\b0 :(const char *) 
\b theField
\b0  \{\
	id			
\b theDocInfo
\b0 ;\
	HashTable	*
\b docInfoTable;
\b0 \
	long 		key;\
	NXHashState state;\
	static List*
\b theList
\b0 =nil;\
	\
	if (!theList) 
\b theList
\b0  = [[
\b List
\b0  alloc] 
\b init
\b0 ];\
	[
\b theList
\b0  
\b empty
\b0 ];\
	docInfoTable = [
\b eTDocInfo docInfoTable
\b0 ];\
	
\b state
\b0  = [docInfoTable 
\b initState
\b0 ];\
	
\b while
\b0  ([docInfoTable 
\b nextState
\b0 :&state key:(void **)&key\
			value: (void**) &
\b theDocInfo
\b0 ])\
		if ([
\b theDocInfo
\b0  
\b searchFor
\b0 :NXUniqueString(
\b theQuery
\b0 )\
						
\b in
\b0 :NXUniqueString(
\b theField
\b0 )])\
			[
\b theList
\b0  
\b addObject
\b0 :
\b theDocInfo
\b0 ];\
	
\b return
\b0  
\b theList
\b0 ;\
\}\
\
- (
\b List
\b0  *)
\b queryByPanel
\b0  \{\
	id			
\b theDocInfo
\b0 ;\
	HashTable	*
\b docInfoTable;
\b0 \
	long 		key;\
	NXHashState state;\
	static List*
\b theList
\b0 =nil;\
	const 
\b char 
\b0 *iqA, *iqD, *iqK, *iqC;\
	\
	if (!theList) 
\b theList
\b0  = [[
\b List
\b0  alloc] 
\b init
\b0 ];\
	[
\b theList
\b0  
\b empty
\b0 ];\
	docInfoTable = [
\b eTDocInfo docInfoTable
\b0 ];\
	
\b state
\b0  = [docInfoTable 
\b initState
\b0 ];\
	iqA = [
\b iqAuthor
\b0  stringValue];\
	iqD = [
\b iqDocTitle
\b0  stringValue];\
	iqK = [
\b iqKeyword
\b0  stringValue];\
	iqC = [
\b iqComments
\b0  stringValue];\
	
\b while
\b0  ([docInfoTable 
\b nextState
\b0 :&state key:(void **)&key\
			value: (void**) &
\b theDocInfo
\b0 ]) \{\
		if ((*iqA && [
\b theDocInfo
\b0  
\b searchFor
\b0 :iqA in:
\b AUTHOR
\b0 ]) ||\
			(*iqD && [
\b theDocInfo
\b0  
\b searchFor
\b0 :iqD in:
\b DOCTITLE
\b0 ]) ||\
			(*iqK && [
\b theDocInfo
\b0  
\b searchFor
\b0 :iqK in:
\b KEYWORDS
\b0 ]) ||\
			(*iqC && [
\b theDocInfo
\b0  
\b searchFor
\b0 :iqC in:
\b COMMENTS
\b0 ]))\
			[
\b theList
\b0  
\b addObject
\b0 :
\b theDocInfo
\b0 ];\
	\}\
	
\b return
\b0  
\b theList
\b0 ;\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b AppKit Delegate
\b0 \
//\
\
static int 
\b Navigator_docompare
\b0 (eTDocInfo **x, eTDocInfo **y)\
\{\
	return strcasecmp([*(eTDocInfo **)x docTitle],[*(eTDocInfo **)y docTitle]);\
\}\
- (
\b int
\b0 ) 
\b browser
\b0 :theBrowser 
\b fillMatrix
\b0 :theMatrix 
\b inColumn
\b0 :(int) col \{\
	
\b int
\b0 		
\b rows
\b0 ,i,count;\
	id		cell;\
	List *	
\b result
\b0 ;\
	BOOL	allLeaves=NO;\
	\
	
\b if
\b0  (theBrowser == 
\b btBrowser
\b0 ) \{\
		
\b result
\b0  = [self 
\b btList]
\b0 ;\
	\} else 
\b if
\b0  (
\b col
\b0  == 
\b 0
\b0 ) \{\
		if (
\b shouldUseQP
\b0 )\
			
\b result
\b0  = [self 
\b queryByPanel
\b0 ];\

\b 		
\b0 else 
\b result
\b0  = [self 
\b query
\b0 :"^$" 
\b field
\b0 :
\b PARENT
\b0 ]; \
	\} 
\b else
\b0  \{\

\b 		if
\b0  ([[[
\b theBrowser
\b0  matrixInColumn:col-1] 
\b selectedCell
\b0 ] 
\b docID
\b0 ]) \{\

\b 			result
\b0  = [self 
\b query
\b0 :[[[theBrowser matrixInColumn:col-1]\
						selectedCell] 
\b docIDStr
\b0 ] 
\b field
\b0 : 
\b PARENT
\b0 ]; \
		\} 
\b else
\b0  \{\

\b 			result
\b0  = [self 
\b query
\b0 :"
\b *
\b0 " 
\b field
\b0 : "
\b *
\b0 "];\

\b 			allLeaves
\b0  = 
\b YES
\b0 ;\
		\}\
	\}\
	
\b if
\b0  (theBrowser == 
\b navBrowser
\b0 ) \{\
		// 
\i Sort the list alphabetically
\i0 \
		
\b qsort
\b0 (
\b result
\b0 ->dataPtr, 
\b result
\b0 ->numElements,sizeof(
\b id
\b0 ),\
				
\b Navigator_docompare
\b0 );\
	\}\
\
	
\b count
\b0  = [
\b result
\b0  
\b count
\b0 ];\
	
\b if
\b0  ((theBrowser == 
\b navBrowser
\b0 ) 
\b &&
\b0  (
\b col
\b0  == 
\b 0
\b0 ))\{\
		
\b rows
\b0  = 
\b count+1
\b0 ;\
		[theMatrix 
\b renewRows
\b0 :rows cols:1];\
		cell = [theMatrix cellAt:count :0];\
		[
\b cell
\b0  
\b setListAllMode
\b0 ];	// 
\i add the "All Known Documents" entry\

\i0 		[cell setLeaf:NO];\
	\} else \{\
		rows = count;\
		[theMatrix 
\b renewRows
\b0 :rows cols:1];\
	\}\
\
	for (i=0; i <count; i++) \{\
		
\b cell
\b0  = [theMatrix 
\b cellAt
\b0 :
\b i
\b0  :0];\
		[
\b cell
\b0  
\b setDocInfo
\b0 :[
\b result
\b0  
\b objectAt
\b0 :
\b i
\b0 ]];\
		
\b if
\b0  ((theBrowser == 
\b btBrowser
\b0 ) 
\b ||
\b0  
\b allLeaves
\b0 ) [cell 
\b setLeaf
\b0 :
\b YES
\b0 ];\
		
\b else
\b0  [cell 
\b setLeaf
\b0 :
\b !
\b0 [self 
\b isParent
\b0 :[
\b result
\b0  
\b objectAt
\b0 :
\b i
\b0 ]]];\
	\}\
	
\b return
\b0  
\b rows
\b0 ;\
\}\
\
- 
\b windowDidUpdate
\b0 :sender \{\
	static char navPath[2*MAXPATHLEN]; //
\i  I suspect the previous declaration of 3k on the stack (non-static) was smashing structures randomly.
\i0 \
	\
	[navPanel 
\b disableFlushWindow
\b0 ];\
	if (navDirty) \{\
		[
\b navBrowser
\b0  
\b getPath
\b0 :navPath 
\b toColumn
\b0 :([navBrowser 
\b lastColumn
\b0 ]+1)];\
		[
\b navBrowser
\b0  
\b loadColumnZero
\b0 ];\
		[
\b navBrowser
\b0  
\b setPath
\b0 :navPath];\
		navDirty = NO;\
	\}\
	if (btDirty) \{\
		[
\b btBrowser
\b0  
\b loadColumnZero
\b0 ];\
		btDirty = NO;\
	\}\
	[[navPanel 
\b reenableFlushWindow
\b0 ] 
\b flushWindow
\b0 ];\
	return self;\
\}\
\

\i @end
}
